/**
    Copyright (c) 2010 yakiimo02
    Distributed under the New BSD License.
    See included license.txt or http://www.yakiimo3d.com/NewBSDLicense.txt
**/

/**
    @file   
    @brief  Rheinhard global operator tonemapping.
    @author yakiimo02
    @date    2010/03/07
*/

struct QuadVS_Input
{
    float4 Pos : POSITION;
    float2 Tex : TEXCOORD0;
};

struct QuadVS_Output
{
    float4 Pos : SV_POSITION;              
    float2 Tex : TEXCOORD0;
};

/** Vertex Shader */

QuadVS_Output QuadVS( QuadVS_Input Input )
{
    QuadVS_Output Output;
    Output.Pos = Input.Pos;
    Output.Tex = Input.Tex;
    return Output;
}

/** Pixel Shader */

StructuredBuffer<uint4> buffer : register( t0 );
SamplerState LinearSampler        : register( s0 );

cbuffer cbPS : register( b0 )
{
    float        g_logAvgLum;        // log average luminance
    float        g_lumMax;            // luminance max value
    float        g_middleKey;        // tonemap middle key
    int            g_toneMapEnable;    // enable tonemapping
    int            g_controlBurnOut;    // control luminance burnout 
    int            nInputWidth;        // input image width
    int            nInputHeight;        // input image height
};

float4 QuadPS( QuadVS_Output Input ) : SV_TARGET
{
    float2 index2 = float2( nInputWidth, nInputHeight ) * Input.Tex.xy;
    int index = nInputWidth*(int)index2.y + index2.x;
    float3 sceneColor = buffer[ index ].xyz;
    
    // tonemapping is disabled
    if( g_toneMapEnable == 0 ) {
        return float4( sceneColor.xyz, 1 );
    }
            
    // RGB -> XYZ conversion
    const float3x3 RGB2XYZ = { 0.5141364, 0.3238786,  0.16036376,
                             0.265068,  0.67023428, 0.06409157,
                             0.0241188, 0.1228178,  0.84442666 };                                    
    float3 XYZ = mul( RGB2XYZ, sceneColor.rgb );
  
    // XYZ -> Yxy conversion
    float xyzSum = XYZ.r + XYZ.g + XYZ.b;
    float3 Yxy;
    Yxy.r = XYZ.g;                        // luminance Y
    Yxy.g = XYZ.r / ( xyzSum );            // x = X / (X + Y + Z)
    Yxy.b = XYZ.g / ( xyzSum );            // y = Y / (X + Y + Z)
    
    // cube the luminance to increase contrast (modification for Buddhabrot impl)
    Yxy.r = (Yxy.r*Yxy.r*Yxy.r);
    
    // Map the average luminance to the middle key zone
    float Lp = Yxy.r * g_middleKey / g_logAvgLum;                       
    
    // scale the luminance 
    if( g_controlBurnOut ) {
        Yxy.r = ( Lp * ( 1.0 + Lp /( g_lumMax * g_lumMax ) ) ) / ( 1.0 + Lp );
    }
    else {
        Yxy.r = ( Lp ) / ( 1.0 + Lp );
    }
  
    // Yxy -> XYZ conversion
    XYZ.r = Yxy.r * Yxy.g / Yxy. b;                        // X = Y * x / y
    XYZ.g = Yxy.r;                                        // luminance Y
    XYZ.b = Yxy.r * ( 1.0 - Yxy.g - Yxy.b ) / Yxy.b;    // Z = Y * (1-x-y) / y
    
    // XYZ -> RGB conversion
    const float3x3 XYZ2RGB  = { 2.5651,-1.1665,-0.3986,
                              -1.0217, 1.9777, 0.0439, 
                               0.0753, -0.2543, 1.1892};
    sceneColor.rgb = mul(XYZ2RGB, XYZ);
    
    // gamma correct before outputting
    // using SRGB format backbuffer so no need to gamma correct.
    // return float4( pow( sceneColor, 1.0/2.2 ), 1.0 );
    
    return float4( sceneColor, 1.0 );
}